﻿using System;
using System.Collections.Generic;
using System.Text;
using UlsStudio.Engine;
using UlsStudio.Engine.SqlParsers;
using UlsStudio.AppCore.Events;

namespace UlsStudio.AppCore
{
    /// <summary>
    /// The heart of an application instance. 
    /// Contains logging data and handles main application events, methods and properties (new query, new stats, query history, etc).
    /// </summary>
    public class ApplicationEngine
    {
        #region Constructor & Privates

        private SimpleSqlSelectStatementInfo _currentQuery = null;
        private readonly UlsDataSet _ulsData = null;
        private readonly GridDataset _gridHelper = null;
        private LinkedList<SimpleSqlSelectStatementInfo> _queryHistory = null;
         
        public ApplicationEngine()
        {
            // Vars init
            this._ulsData = new UlsDataSet();
            this._gridHelper = new GridDataset(this);
            this._queryHistory = new LinkedList<SimpleSqlSelectStatementInfo>();

            // Settings & statuses
            this.RealTimeSettings = new RealTimeScanSettings();
            this.FileSetLoadSettings = new UlsScanSettings();
            this.LastLoadingStatus = new RealTimeLoadingStatus();
            this.LoadMode = LoadMode.RealTime;      // Default to real-time
            this.CurrentQueryHistoryDepth = 0;
        }

        #endregion

        #region Query History

        /// <summary>
        /// Set new query and add old one to history list
        /// </summary>
        public void SetNewQuery(SimpleSqlSelectStatementInfo newQuery)
        {
            SetNewQuery(newQuery, false);
        }

        /// <summary>
        /// Set new query and either delete history or add old current query to history
        /// </summary>
        public void SetNewQuery(SimpleSqlSelectStatementInfo newQuery, bool deleteHistory)
        {
            if (deleteHistory)
            {
                _queryHistory.Clear();
            }
            else
            {
                // Is this new query being inserted "before" the latest query in the history list?
                if (this.CurrentQueryHistoryDepth > 0)
                {
                    for (int i = 0; i < this.CurrentQueryHistoryDepth; i++)
                    {
                        this.QueryHistory.RemoveLast();
                    }
                    this.CurrentQueryHistoryDepth = 0;
                }
                
                // Add new query to end of history list
                _queryHistory.AddLast(newQuery.Clone());
            }

            // Set new current query
            _currentQuery = newQuery;
        }

        /// <summary>
        /// How far back in history is the current query. 0 = latest query.
        /// </summary>
        public int CurrentQueryHistoryDepth { get; set; }

        /// <summary>
        /// Returns a query from before
        /// </summary>
        /// <param name="iterationsFromCurrent">How many queries before to get from the latest query</param>
        /// <returns></returns>
        public SimpleSqlSelectStatementInfo GoBackInHistory(int iterationsFromCurrent)
        {
            int goBackFromLatestCount = iterationsFromCurrent + this.CurrentQueryHistoryDepth;
            if (iterationsFromCurrent < 1 || goBackFromLatestCount > this.QueryHistory.Count)
            {
                throw new ArgumentOutOfRangeException("iterationsFromLast");
            }

            // Find node
            LinkedListNode<SimpleSqlSelectStatementInfo> node = this.QueryHistory.Last;
            
            for (int i = 0; i < goBackFromLatestCount; i++)
            {
                node = node.Previous;
            }

            // Set current query as this
            SimpleSqlSelectStatementInfo queryCopy = node.Value.Clone();
            _currentQuery = queryCopy;

            // Remember history position
            this.CurrentQueryHistoryDepth += iterationsFromCurrent;

            // Return to GUI
            return queryCopy;
        }

        /// <summary>
        /// Moves forward to a query that we have earlier in history.
        /// </summary>
        /// <param name="iterationsFromCurrent">How many queries before to get from the latest query</param>
        /// <returns></returns>
        public SimpleSqlSelectStatementInfo GoForwardInHistory(int iterationsFromCurrent)
        {
            if (iterationsFromCurrent < 1 || iterationsFromCurrent > this.CurrentQueryHistoryDepth)
            {
                throw new ArgumentOutOfRangeException("iterationsFromCurrent");
            }

            // Find last node & navigate to current history position
            LinkedListNode<SimpleSqlSelectStatementInfo> node = this.QueryHistory.Last;
            for (int i = 0; i < this.CurrentQueryHistoryDepth; i++)
            {
                node = node.Previous;
            }

            // Now navigate forward X times
            for (int i = 0; i < iterationsFromCurrent; i++)
            {
                node = node.Next;
            }

            // Set current query as this 
            SimpleSqlSelectStatementInfo queryCopy = node.Value.Clone();
            _currentQuery = queryCopy;

            // Remember history position
            this.CurrentQueryHistoryDepth = this.CurrentQueryHistoryDepth - iterationsFromCurrent;

            // Return to GUI
            return queryCopy;
        }

        
        #endregion

        #region Properties
        /// <summary>
        /// A parsed version of ViewQuerySql property
        /// </summary>
        public SimpleSqlSelectStatementInfo CurrentQuery { get { return _currentQuery; } }

        
        /// <summary>
        /// List of queries in history
        /// </summary>
        public LinkedList<SimpleSqlSelectStatementInfo> QueryHistory
        {
            get { return _queryHistory; }
            set { _queryHistory = value; }
        }

        /// <summary>
        /// File-set or real-time ULS?
        /// </summary>
        public LoadMode LoadMode { get; set; }

        /// <summary>
        /// Data used by application
        /// </summary>
        public UlsDataSet UlsData { get { return _ulsData; }  }

        /// <summary>
        /// Operates grid-cache etc
        /// </summary>
        public GridDataset GridHelper { get { return _gridHelper; } }

        

        /// <summary>
        /// Loading is happening - referenced in MainForm_FormClosing, uses LoadStatus
        /// </summary>
        public bool LoadInProgress
        {
            get
            {
                if (this.LastLoadingStatus == null)
                    return false;
                else
                {
                    if (this.LastLoadingStatus is FileSetJobLoadingStatus)
                    {
                        // If the user's clicked cancel then load is complete
                        return (!((FileSetJobLoadingStatus)this.LastLoadingStatus).UserCancelledLoad)
                            && !this.LastLoadingStatus.LoadComplete;
                    }
                    else
                    {
                        return !this.LastLoadingStatus.LoadComplete;
                    }

                }
            }
        }

        /// <summary>
        /// Settings used to load data last. By default is RealTimeScanSettings
        /// </summary>
        public UlsScanSettings FileSetLoadSettings { get; set; }

        /// <summary>
        /// Settings used for the RTS monitor
        /// </summary>
        public RealTimeScanSettings RealTimeSettings { get; set; }

        /// <summary>
        /// Used by background timer to read last-loaded stats
        /// </summary>
        /// 

        private AbstractLoadingStatus _lastLoadingStatus = null;


        public AbstractLoadingStatus LastLoadingStatus
        {
            get { return _lastLoadingStatus; }
            set { _lastLoadingStatus = value; }
        }

        #endregion

        #region Custom Even Definitions

        /// <summary>
        /// Query has been validated
        /// </summary>
        public event EventHandler<QueryValidationResultEventArgs> FilterValidated;
        private void OnFilterValidated(QueryValidationResultEventArgs newFilter)
        {
            if (FilterValidated != null)
            {
                FilterValidated(this, newFilter);
            }
        }

        /// <summary>
        /// New stats have been returned
        /// </summary>
        public event EventHandler<StatisticsEventArgs> NewStatistics;
        private void OnNewStatistics(StatisticsEventArgs stats)
        {
            if (NewStatistics != null)
            {
                NewStatistics(this, stats);
            }
        }

        #endregion

        #region Filtering

        /// <summary>
        /// Sets a new query for the program
        /// </summary>
        /// <param name="newDisplayFilter">New query to set</param>
        /// <returns></returns>
        public bool SetDisplayFilter(SimpleSqlSelectStatementInfo newDisplayFilter)
        {
            Logging.Writeline("ApplicationEngine.SetDisplayFilter - Testing new display query '{0}'", newDisplayFilter);

            // Refresh with a test query
            string errorMessage = string.Empty;
            bool queryIsValid = QueryIsValid(newDisplayFilter, out errorMessage);

            // All good - call end thread
            OnFilterValidated(new QueryValidationResultEventArgs(newDisplayFilter, queryIsValid, errorMessage));
            

            return queryIsValid;
        }
        #endregion

        #region Stats

        /// <summary>
        /// Threaded version of UpdateStats(bool fullUpdate)
        /// </summary>
        public void UpdateRecordStats(object fullUpdate)
        {
            UpdateRecordStats((bool)fullUpdate);
        }
        public void UpdateRecordStats(bool fullUpdate)
        {
            BaseDataStats stats = null;

            // Get full stats or just basic record count?
            if (fullUpdate)
            {
                stats = this.UlsData.GetRecordStats(this.CurrentQuery);
            }
            else
            {
                int totalFilterRecords = this.UlsData.GetItemCount(this.CurrentQuery);
                int totalRecordsInserted = this.UlsData.GetItemCount();
                stats = new BaseDataStats(totalRecordsInserted, totalFilterRecords);
            }

            // Return to main thread
            OnNewStatistics(new StatisticsEventArgs(stats));
            //this.Form.EndUpdateRecordStats(stats);
        }

        #endregion

        #region Query Functions

        /// <summary>
        /// Returns whether a query is valid by doing an item-count
        /// </summary>
        public bool QueryIsValid(SimpleSqlSelectStatementInfo testQuery, out string errorMessage)
        {
            // Refresh with a test query
            try
            {
                this.UlsData.GetItemCount(testQuery);
                errorMessage = string.Empty;
            }
            catch (System.Data.Common.DbException ex)
            {
                errorMessage = ex.Message;
                return false;
            }

            return true;
        }

        #endregion

        #region Misc

        /// <summary>
        /// Returns whether this client machine is (likely) connected to MSFT corpnet.
        /// Used for the Kudos function.
        /// </summary>
        public bool ClientIsConnectedToCorpNet()
        {
            System.Net.IPHostEntry kudosSiteDNS = new System.Net.IPHostEntry();
            try
            {
                kudosSiteDNS = System.Net.Dns.GetHostEntry("kudos");
            }
            catch (Exception)
            {
                return false;
            }

            return (kudosSiteDNS.AddressList.Length > 0);
        }

        #endregion
    }
}
